/** 
 * Copyright (C) 2018 Jeebiz (http://jeebiz.net).
 * All Rights Reserved. 
 */
package net.jeebiz.cloud.extras.core.setup.security;

import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.authentication.AccountExpiredException;
import org.springframework.security.authentication.AuthenticationServiceException;
import org.springframework.security.authentication.BadCredentialsException;
import org.springframework.security.authentication.CredentialsExpiredException;
import org.springframework.security.authentication.DisabledException;
import org.springframework.security.authentication.InternalAuthenticationServiceException;
import org.springframework.security.authentication.LockedException;
import org.springframework.security.boot.biz.SpringSecurityBizMessageSource;
import org.springframework.security.boot.biz.exception.AuthResponseCode;
import org.springframework.security.boot.biz.exception.AuthenticationCaptchaExpiredException;
import org.springframework.security.boot.biz.exception.AuthenticationCaptchaIncorrectException;
import org.springframework.security.boot.biz.exception.AuthenticationCaptchaInvalidException;
import org.springframework.security.boot.biz.exception.AuthenticationCaptchaNotFoundException;
import org.springframework.security.boot.biz.exception.AuthenticationCaptchaSendException;
import org.springframework.security.boot.biz.exception.AuthenticationMethodNotSupportedException;
import org.springframework.security.boot.biz.exception.AuthenticationOverRetryRemindException;
import org.springframework.security.boot.biz.exception.AuthenticationTokenExpiredException;
import org.springframework.security.boot.biz.exception.AuthenticationTokenIncorrectException;
import org.springframework.security.boot.biz.exception.AuthenticationTokenInvalidException;
import org.springframework.security.boot.biz.exception.AuthenticationTokenNotFoundException;
import org.springframework.security.boot.biz.exception.ExternalAuthenticationServiceException;
import org.springframework.security.boot.jwt.exception.AuthenticationJwtExpiredException;
import org.springframework.security.boot.jwt.exception.AuthenticationJwtIncorrectException;
import org.springframework.security.boot.jwt.exception.AuthenticationJwtInvalidException;
import org.springframework.security.boot.jwt.exception.AuthenticationJwtIssuedException;
import org.springframework.security.boot.jwt.exception.AuthenticationJwtNotFoundException;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import net.jeebiz.cloud.api.ApiCode;
import net.jeebiz.cloud.api.ApiRestResponse;
import net.jeebiz.cloud.autoconfigure.webmvc.ExceptinHandler;

@ControllerAdvice
public class SecurityExceptinHandler extends ExceptinHandler {

	protected final MessageSourceAccessor messages = SpringSecurityBizMessageSource.getAccessor();

	// --- Security Default Error ---

	/**
	 * 401 (Unauthorized) 
	 */
	@ExceptionHandler({ AccessDeniedException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> accessDeniedException(AccessDeniedException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiCode.SC_ACCESS_DENIED.toResponse(), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized) 
	 */
	@ExceptionHandler({ AuthenticationException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationException(AuthenticationException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHC_FAIL.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHC_FAIL.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}


	/**
	 * 401 (Unauthorized) 
	 */
	@ExceptionHandler({ AuthenticationServiceException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationServiceException(AuthenticationServiceException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized) 
	 */
	@ExceptionHandler({ InternalAuthenticationServiceException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> internalAuthenticationServiceException(InternalAuthenticationServiceException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ UsernameNotFoundException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> usernameNotFoundException(UsernameNotFoundException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHC_USER_NOT_FOUND.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHC_USER_NOT_FOUND.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ BadCredentialsException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> badCredentialsException(BadCredentialsException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CREDENTIALS_INCORRECT.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CREDENTIALS_INCORRECT.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ DisabledException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> disabledException(DisabledException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHC_USER_DISABLED.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHC_USER_DISABLED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ LockedException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> lockedException(LockedException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHC_USER_LOCKED.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHC_USER_LOCKED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AccountExpiredException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> accountExpiredException(AccountExpiredException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_USER_EXPIRED.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHC_USER_EXPIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ CredentialsExpiredException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> credentialsExpiredException(CredentialsExpiredException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CREDENTIALS_EXPIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CREDENTIALS_EXPIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	// --- Security Biz Error ---
	
	/**
	 * 401 (Unauthorized) 
	 */
	@ExceptionHandler({ ExternalAuthenticationServiceException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> externalAuthenticationServiceException(ExternalAuthenticationServiceException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>(ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getCode(),
				messages.getMessage(AuthResponseCode.SC_AUTHZ_THIRD_PARTY_SERVICE.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationMethodNotSupportedException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationMethodNotSupportedException( AuthenticationMethodNotSupportedException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_METHOD_NOT_ALLOWED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_METHOD_NOT_ALLOWED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationOverRetryRemindException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationOverRetryRemindException( AuthenticationOverRetryRemindException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_OVER_RETRY_REMIND.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_OVER_RETRY_REMIND.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationCaptchaSendException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationCaptchaSendException( AuthenticationCaptchaSendException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CAPTCHA_SEND_FAIL.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CAPTCHA_SEND_FAIL.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationCaptchaNotFoundException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationCaptchaNotFoundException( AuthenticationCaptchaNotFoundException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CAPTCHA_REQUIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CAPTCHA_REQUIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationCaptchaExpiredException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationCaptchaExpiredException( AuthenticationCaptchaExpiredException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CAPTCHA_REQUIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CAPTCHA_EXPIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationCaptchaInvalidException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationCaptchaInvalidException( AuthenticationCaptchaInvalidException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CAPTCHA_INVALID.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CAPTCHA_INVALID.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationCaptchaIncorrectException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationCaptchaIncorrectException( AuthenticationCaptchaIncorrectException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHC_CAPTCHA_INCORRECT.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHC_CAPTCHA_INCORRECT.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationTokenNotFoundException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationTokenNotFoundException( AuthenticationTokenNotFoundException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_REQUIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_REQUIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationTokenExpiredException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationTokenExpiredException(AuthenticationTokenExpiredException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_EXPIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_EXPIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationTokenInvalidException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationTokenInvalidException( AuthenticationTokenInvalidException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_INCORRECT.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_INVALID.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationTokenIncorrectException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationTokenIncorrectException(AuthenticationTokenIncorrectException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_INCORRECT.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_INCORRECT.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	
	// --- Security JWT Error ---
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationJwtIssuedException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationJwtIssuedException( AuthenticationJwtIssuedException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_ISSUED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_ISSUED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationJwtNotFoundException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationJwtNotFoundException( AuthenticationJwtNotFoundException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_REQUIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_REQUIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationJwtExpiredException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationJwtExpiredException( AuthenticationJwtExpiredException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_EXPIRED.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_EXPIRED.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}

	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationJwtInvalidException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationJwtInvalidException( AuthenticationJwtInvalidException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_INVALID.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_INVALID.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
	/**
	 * 401 (Unauthorized)
	 */
	@ExceptionHandler({ AuthenticationJwtIncorrectException.class })
	@ResponseBody
	public ResponseEntity<ApiRestResponse<String>> authenticationJwtIncorrectException(AuthenticationJwtIncorrectException ex) {
		this.logException(ex);
		return new ResponseEntity<ApiRestResponse<String>>( ApiRestResponse.of(AuthResponseCode.SC_AUTHZ_TOKEN_INCORRECT.getCode(), 
				messages.getMessage(AuthResponseCode.SC_AUTHZ_TOKEN_INCORRECT.getMsgKey(), ex.getMessage())), HttpStatus.UNAUTHORIZED);
	}
	
}
